<?php
/************
# 简单导出EXCEL数据，用XML格式组装的
# 大数据量时，建议设置临时文件的方式（$this->setCacheFile()）。或是临时提高PHP的内存上限
# lsj
************/


class excelXml
{
        private $header = "<?xml version=\"1.0\" encoding=\"%s\"?\>\n<Workbook xmlns=\"urn:schemas-microsoft-com:office:spreadsheet\" xmlns:x=\"urn:schemas-microsoft-com:office:excel\" xmlns:ss=\"urn:schemas-microsoft-com:office:spreadsheet\" xmlns:html=\"http://www.w3.org/TR/REC-html40\">";

        private $footer = "</Workbook>";

        private $lines = array();#各行的内容

        private $sEncoding;#文件的编码

        private $bConvertTypes;#文件的类型，数字、文本

        private $sWorksheetTitle;#工作簿名称
        
        private $cacheFile = '';#缓存文件路径
        
        private $cacheFileHandle = null;#打开缓存文件的句柄

        public function __construct($sEncoding = 'UTF-8', $bConvertTypes = false, $sWorksheetTitle = 'Table1')
        {
            $this->bConvertTypes = $bConvertTypes;
            $this->setEncoding($sEncoding);
            $this->setWorksheetTitle($sWorksheetTitle);
            return true;
        }

        public function setEncoding($sEncoding)
        {
            $this->sEncoding = $sEncoding;
            return true;
        }

        public function setWorksheetTitle ($title)
        {
            $title = preg_replace ("/[\\\|:|\/|\?|\*|\[|\]]/", "", $title);
            $title = substr ($title, 0, 31);
            $this->sWorksheetTitle = $title;
            return true;
        }
        
        public function setCacheFile($theFile = '')
        {
            $this->cacheFile = $theFile;
            if('' != $this->cacheFile){
                if($this->cacheFileHandle = @fopen($this->cacheFile, 'wb')){
                    fwrite($this->cacheFileHandle, stripslashes(sprintf($this->header, $this->sEncoding)));
                    fwrite($this->cacheFileHandle, "\n<Worksheet ss:Name=\"" . $this->sWorksheetTitle . "\">\n<Table>\n");
                    return true;
                } 
                return false;
            }
            return true;
        }

        private function addRow ($array)
        {
            $cells = "";
            foreach ($array as $k => $v){
                    $type = 'String';
                    if ($this->bConvertTypes === true && is_numeric($v)){
                            $type = 'Number';
                    }
                    $v = htmlentities($v, ENT_COMPAT, $this->sEncoding);
                    $cells .= "<Cell><Data ss:Type=\"$type\">" . $v . "</Data></Cell>\n"; 
            }
            
            if('' != $this->cacheFile && $this->cacheFileHandle){
                    fwrite($this->cacheFileHandle, "<Row>\n" . $cells . "</Row>\n");
            }else{
                    $this->lines[] = "<Row>\n" . $cells . "</Row>\n";
            }
            
            return true;    
        }

        public function addArray ($array)
        {
            foreach ($array as $k => $v){
                    $this->addRow ($v);
            }
        }

        public function export ($filename = 'excelData')
        {
            $filename = preg_replace('/[^aA-zZ0-9\_\-]/', '', $filename);
 
            header("Content-Type: application/vnd.ms-excel; charset=" . $this->sEncoding);
            header("Content-Disposition: inline; filename=\"" . $filename . ".xls\"");
            
            if('' != $this->cacheFile && $this->cacheFileHandle){
                fwrite($this->cacheFileHandle, "</Table>\n</Worksheet>\n".$this->footer);
                fclose($this->cacheFileHandle);
                readfile($this->cacheFile);
            }else{
                echo stripslashes (sprintf($this->header, $this->sEncoding));
                echo "\n<Worksheet ss:Name=\"" . $this->sWorksheetTitle . "\">\n<Table>\n";
                foreach ($this->lines as $line){
                    echo $line;
                }
                echo "</Table>\n</Worksheet>\n";
                echo $this->footer;
            }
        }
        
		/*
		* 这个方法还有问题，当设置临时文件时没生效
		*/
        public function save ($file)
        {
            $filepath = dirname($file);
            $filename = basename($file);
            if(!is_dir($filepath)) $this->makedirs($filepath);
            $filename = preg_replace('/[^aA-zZ0-9\_\-]/', '', $filename);
            
            $content = stripslashes (sprintf($this->header, $this->sEncoding));
            $content .= "\n<Worksheet ss:Name=\"" . $this->sWorksheetTitle . "\">\n<Table>\n";
            if('' != $this->cacheFile && $this->cacheFileHandle){
                fclose($this->cacheFileHandle);
                $content .= file_get_contents($this->cacheFile);
            }else{
                foreach ($this->lines as $line){
                    $content .= $line;
                }
            }
            $content .= "</Table>\n</Worksheet>\n";
            $content .= $this->footer;
            
            return file_put_contents($file);
        }
        
        private function makedirs ($dir)
        {
            if(!$dir) return false;
            $ds = '/';
            $dir = str_replace("\\", $ds, $dir);
            $dirs  = explode($ds, trim($dir, $ds));
            $nd = '';
            foreach($dirs as $d){
                if(empty($d)) continue;
                $nd .= $ds . $d;
                if(is_dir($nd)) continue;
                if(!mkdir($nd)) return false;
            }
            return true;
        }

}

?>